#include "Cryptoflex8Plugin.h"


Cryptoflex8Plugin::Cryptoflex8Plugin() : Plugin("cryptoflex8")
{
}

void Cryptoflex8Plugin::InitializePlugin( PluginSpMapSp plugins, INameResolutorSp nameResolutor )
{
	Plugin::InitializePlugin(plugins, nameResolutor);

	// Get useful references to type descriptors
	AccessConditionTd = descriptorsRepository->GetEnumTypeDescriptor("cryptoflex8.AccessCondition");
	AvailablePINsTd = descriptorsRepository->GetEnumTypeDescriptor("cryptoflex8.AvailablePINs");
	ChvDataTd = descriptorsRepository->GetEnumTypeDescriptor("cryptoflex8.ChvData");
	ChvStatusTd = descriptorsRepository->GetEnumTypeDescriptor("cryptoflex8.ChvStatus");
	CommandRestrictionsTd = descriptorsRepository->GetEnumTypeDescriptor("cryptoflex8.CommandRestrictions");
	DirInfoTd = descriptorsRepository->GetStructTypeDescriptor("cryptoflex8.DirInfo");
	DirSelectInfoTd = descriptorsRepository->GetStructTypeDescriptor("cryptoflex8.DirSelectInfo");
	FileInfoTd = descriptorsRepository->GetStructTypeDescriptor("cryptoflex8.FileInfo");
	FileStatusTd = descriptorsRepository->GetEnumTypeDescriptor("cryptoflex8.FileStatus");
	FileTypeTd = descriptorsRepository->GetEnumTypeDescriptor("cryptoflex8.FileType");
	VDirInfoTd = descriptorsRepository->GetVectorTypeDescriptor("cryptoflex8.VDirInfo");
	VFileInfoTd = descriptorsRepository->GetVectorTypeDescriptor("cryptoflex8.VFileInfo");
}

void Cryptoflex8Plugin::ExecuteCommand(CommandSp cmd)
{
	Plugin::ExecuteCommand(cmd);

	vars = SValueSpMapSp(new SValueSpMap());

	if (commandName != "RetrieveCLA")
	{
		(*vars)["connection"] = inputs->Get<SNumber>("connection");
		(*vars)["protocol"] = inputs->Get<SEnum>("protocol");
	}

	// Call the right command handler
	if ( commandName == "ChangeCHV" )	Cryptoflex8_ChangeCHV();
	else if ( commandName == "CreateDF" )	Cryptoflex8_CreateDF();
	else if ( commandName == "CreateEF" )	Cryptoflex8_CreateEF();
	else if ( commandName == "CreateFile" )	Cryptoflex8_CreateFile();
	else if ( commandName == "DeleteFile" )	Cryptoflex8_DeleteFile();
	else if ( commandName == "DirNext" )	Cryptoflex8_DirNext();
	else if ( commandName == "GetFilesInfo" )	Cryptoflex8_GetFilesInfo();
	else if ( commandName == "GetResponse" )	Cryptoflex8_GetResponse();
	else if ( commandName == "Invalidate" )	Cryptoflex8_Invalidate();
	else if ( commandName == "ReadBinary" )	Cryptoflex8_ReadBinary();
	else if ( commandName == "Rehabilitate" )	Cryptoflex8_Rehabilitate();
	else if ( commandName == "RetrieveCLA" )	Cryptoflex8_RetrieveCLA();
	else if ( commandName == "Select" )	Cryptoflex8_Select();
	else if ( commandName == "SelectDF" )	Cryptoflex8_SelectDF();
	else if ( commandName == "SelectEF" )	Cryptoflex8_SelectEF();
	else if ( commandName == "UnblockCHV" )	Cryptoflex8_UnblockCHV();
	else if ( commandName == "UpdateBinary" )	Cryptoflex8_UpdateBinary();
	else if ( commandName == "VerifyCHV" )	Cryptoflex8_VerifyCHV();
	else if ( commandName == "VerifyKey" )	Cryptoflex8_VerifyKey();
	else throw SPluginException("Error: command not implemented by plugin.");
}

void Cryptoflex8Plugin::Cryptoflex8_ChangeCHV()
{
	// Insert plugin operations here
	string input = Utility::Format("0xF02400{0,2nx}10{1,8nx}{2,8nx}", "chvType oldCHV newCHV", inputs);
	SVectorSp receiveBuffer = ExecutePcscTransmit(input);

	// Setting outputs
	BYTE sw1 = (BYTE) receiveBuffer->Get<SByte>(0)->Get();
	BYTE sw2 = (BYTE) receiveBuffer->Get<SByte>(1)->Get();
	SetCommonOutputs(sw1, sw2);
}

void Cryptoflex8Plugin::Cryptoflex8_CreateDF()
{
	// Input preparation   
	SStructSp dirInfo  = inputs->Get<SStruct>("dirInfo");
	(*vars)["dirInfo"] = dirInfo;

	unsigned char k1 = dirInfo->Get<SByte>("dirNextKeyNum")->Get();
	unsigned char k2 = dirInfo->Get<SByte>("deleteKeyNum")->Get();
	unsigned char k3 = dirInfo->Get<SByte>("createKeyNum")->Get();

	(*vars)["keyByte1"] = SByte::Create( k1 * 16 + 0x0F );
	(*vars)["keyByte2"] = SByte::Create( k2 * 16 + k3 );

	// Insert plugin operations here

	string listVars = 
		"dirInfo.fileSize dirInfo.fileID "
		"dirInfo.dirNextAC dirInfo.deleteAC dirInfo.createAC dirInfo.fileStatus "
		"keyByte1 keyByte2 ";

	string input = Utility::Format(
		"0xF0E0000010FFFF{0,4nx}{1,4nx}38FF{2,1nx}F{3,1nx}{4,1nx}FF{5,2nx}03{6,2nx}{7,2nx}FF", 
		listVars, vars);

	SVectorSp receiveBuffer = ExecutePcscTransmit(input);	

	// Setting outputs
	BYTE sw1 = (BYTE) receiveBuffer->Get<SByte>(0)->Get();
	BYTE sw2 = (BYTE) receiveBuffer->Get<SByte>(1)->Get();
	SetCommonOutputs(sw1, sw2);
}

void Cryptoflex8Plugin::Cryptoflex8_CreateEF()
{
	// Input preparation   
	SByteSp init  = inputs->Get<SByte>("init");
	SStructSp fileInfo  = inputs->Get<SStruct>("fileInfo");

	(*vars)["init"] = init;
	
	if ( fileInfo->IsSet("numRecords") )
	{
		(*vars)["numRecords"] = fileInfo->Get<SByte>("numRecors");
	}
	else
	{
		(*vars)["numRecords"] = SByte::Create(0);
	}

	(*vars)["fileInfo"] = fileInfo;

	string fileTypeValue = fileInfo->Get<SEnum>("fileType")->Get();

	if ( fileTypeValue == "Dedicated")
	{
		throw SPluginException("Use the CreateDF() command to create directory.");
	}

	unsigned char k1 = fileInfo->Get<SByte>("readBinarySeekKeyNum")->Get();
	unsigned char k2 = fileInfo->Get<SByte>("updateBinaryDecreaseKeyNum")->Get();
	unsigned char k3 = fileInfo->Get<SByte>("increaseKeyNum")->Get();
	unsigned char k4 = fileInfo->Get<SByte>("createRecordKeyNum")->Get();
	unsigned char k5 = fileInfo->Get<SByte>("rehabilitateKeyNum")->Get();
	unsigned char k6 = fileInfo->Get<SByte>("invalidateKeyNum")->Get();

	(*vars)["keyByte1"] = SByte::Create( k1 * 16 + k2 );
	(*vars)["keyByte2"] = SByte::Create( k3 * 16 + k4 );
	(*vars)["keyByte3"] = SByte::Create( k5 * 16 + k6 );

	string listVars = 
		"init numRecords length "
		"fileInfo.fileSize fileInfo.fileID fileInfo.fileType fileInfo.restrictions "
		"fileInfo.readBinarySeekAC fileInfo.updateBinaryDecreaseAC fileInfo.increaseAC "
		"fileInfo.createRecordAC fileInfo.rehabilitateAC fileInfo.invalidateAC fileInfo.fileStatus "
		"keyByte1 keyByte2 keyByte3 ";

	string input = "";

	if ( fileTypeValue == "Transparent" || fileTypeValue == "LinearVariable")
	{
		(*vars)["length"] = SByte::Create(0x10);

		input = Utility::Format(
			"0xF0E0{0,2nx}{1,2nx}{2,2nx}FFFF{3,4nx}{4,4nx}{5,2nx}"
			"{6,2nx}{7,1nx}{8,1nx}{9,1nx}{10,1nx}{11,1nx}{12,1nx}{13,2nx}03{14,2nx}{15,2nx}{16,2nx}", 
			listVars, vars);
	}
	else
	{
		(*vars)["length"] = SByte::Create(0x11);
		listVars += "fileInfo.recordLength";

		input = Utility::Format(
			"0xF0E0{0,2nx}{1,2nx}{2,2nx}FFFF{3,4nx}{4,4nx}{5,2nx}"
		    "{6,2nx}{7,1nx}{8,1nx}{9,1nx}{10,1nx}{11,1nx}{12,1nx}{13,2nx}04{14,nx}{15,2nx}{16,2nx}{17,2nx}", 
			listVars, vars);
	}
	
	// Insert plugin operations here
	SVectorSp receiveBuffer = ExecutePcscTransmit(input);	

	// Setting outputs
	BYTE sw1 = (BYTE) receiveBuffer->Get<SByte>(0)->Get();
	BYTE sw2 = (BYTE) receiveBuffer->Get<SByte>(1)->Get();
	SetCommonOutputs(sw1, sw2);
}

void Cryptoflex8Plugin::Cryptoflex8_CreateFile()
{
	// Insert plugin operations here
	string input = Utility::Format("0xF0E0{0,2nx}{1,2nx}{2,2nx}{3,nx}", "init numRecords length data", inputs);
	SVectorSp receiveBuffer = ExecutePcscTransmit(input);

	
	// Setting outputs
	BYTE sw1 = (BYTE) receiveBuffer->Get<SByte>(0)->Get();
	BYTE sw2 = (BYTE) receiveBuffer->Get<SByte>(1)->Get();
	SetCommonOutputs(sw1, sw2);
}


void Cryptoflex8Plugin::Cryptoflex8_DeleteFile()
{
	// Input preparation   
	SNumberSp id  = inputs->Get<SNumber>("id");

	// Insert plugin operations here
	string input = Utility::Format("0xF0E4000002{0,4nx}", "id", inputs);
	SVectorSp receiveBuffer = ExecutePcscTransmit(input);

	// Setting outputs
	BYTE sw1 = (BYTE) receiveBuffer->Get<SByte>(0)->Get();
	BYTE sw2 = (BYTE) receiveBuffer->Get<SByte>(1)->Get();
	SetCommonOutputs(sw1, sw2);
}

void Cryptoflex8Plugin::Cryptoflex8_GetFilesInfo()
{
	SVectorSp filesInfo = SVector::Create( VFileInfoTd);
	SVectorSp dirsInfo = SVector::Create( VDirInfoTd);
	outputs->Set("filesInfo", filesInfo);
	outputs->Set("dirsInfo", dirsInfo);

	bool moreInfo = true;

	do
	{
		// Save the command status
		PushCommandStatus();

		try
		{
			// Executing the DirNext command
			CommandDescriptorSp dirNextTd = descriptorsRepository->GetCommandDescriptor("cryptoflex8.DirNext");
			CommandSp dirNextCmd = Command::Create(dirNextTd, "", vars);
			ExecuteCommand(dirNextCmd);
			SStructSp dirNextOutputs = dirNextCmd->GetOutputs();

			unsigned char sw1 = dirNextOutputs->Get<SByte>("sw1")->Get();
			unsigned char sw2 = dirNextOutputs->Get<SByte>("sw2")->Get();
			SStructSp fileInfo = dirNextOutputs->Get<SStruct>("fileInfo");
			SStructSp dirInfo = dirNextOutputs->Get<SStruct>("dirInfo");

			//cout << hex << sw1 << endl;
			//cout << hex << sw2 << endl;
			//cout << Utility::GetPrettyPrintVar(fileInfo) << endl;
			//cout << Utility::GetPrettyPrintVar(dirInfo) << endl;

			if ( (sw1 == 0x6A) && (sw2 == 0x82) )
			{
				moreInfo = false;
			}
			else
			{
				// Add the result in the right vector
				if ( fileInfo->IsSet("fileID") )
				{
					filesInfo->AddItem(fileInfo);
				}
				else
				{
					dirsInfo->AddItem(dirInfo);
				}			
			}
		}
		catch(...)
		{
			PopCommandStatus();
			throw SPluginException("GetFileInfo() is not able to execute the command DirNext().");
		}

		// Restore the command status
		PopCommandStatus();

	} while(moreInfo);

	// Setting outputs
	outputs->Set("filesInfo", filesInfo);
	outputs->Set("dirsInfo", dirsInfo);
}

void Cryptoflex8Plugin::Cryptoflex8_GetResponse()
{
	// Insert plugin operations here
	string input = Utility::Format("0xC0C00000{0,2nx}", "length", inputs);
	SVectorSp receiveBuffer = ExecutePcscTransmit(input);

	// Setting outputs
	SVectorSp data = SVector::Create( descriptorsRepository->GetVectorTypeDescriptor("pcsc.Buffer") );

	int len = receiveBuffer->GetLength();
	if ( len > 2)
	{
		for(int i=0; i<(int)receiveBuffer->GetLength()-2; ++i)
		{
			data->AddItem( receiveBuffer->Get(i) );
		}
	}

	outputs->Set("data", data);

	BYTE sw1 = (BYTE) receiveBuffer->Get<SByte>(len-2)->Get();
	BYTE sw2 = (BYTE) receiveBuffer->Get<SByte>(len-1)->Get();
	SetCommonOutputs(sw1, sw2);
}

void Cryptoflex8Plugin::Cryptoflex8_DirNext()
{
	// Insert plugin operations here
	SVectorSp data = ExecutePcscTransmit("0xF0A8000010");

	SStructSp fileInfo = SStruct::Create( FileInfoTd );
	SStructSp dirInfo = SStruct::Create( DirInfoTd );

	// Check if there aren't more files
	if ( data->GetLength() == 2 )
	{
		outputs->Set("dirInfo", dirInfo);
		outputs->Set("fileInfo", fileInfo);
		BYTE sw1 = (BYTE) data->Get<SByte>(0)->Get();
		BYTE sw2 = (BYTE) data->Get<SByte>(1)->Get();
		SetCommonOutputs(sw1, sw2);
		return;
	}

	// Setting outputs
	if ( data->Get<SByte>(4)->Get() == 0x38 )
	{
		// The current file is a Directory
		try
		{
			SNumberSp fileSize = Utility::BuildNumber( data->Get<SByte>(0), data->Get<SByte>(1) );
			dirInfo->Set("fileSize", fileSize);

			SNumberSp fileID = Utility::BuildNumber( data->Get<SByte>(2), data->Get<SByte>(3) );
			dirInfo->Set("fileID", fileID);

			// ACs
			SByteSp b1;
			SByteSp b2;
			Utility::SplitNumber( data->Get<SByte>(6), b1, b2);
			dirInfo->Set("dirNextAC", SEnum::Create(AccessConditionTd, (unsigned long) b1->Get() ));

			Utility::SplitNumber( data->Get<SByte>(7), b1, b2);
			dirInfo->Set("deleteAC", SEnum::Create(AccessConditionTd, (unsigned long) b1->Get() ));
			dirInfo->Set("createAC", SEnum::Create(AccessConditionTd, (unsigned long) b2->Get() ));

			// fileStatus
			dirInfo->Set("fileStatus", SEnum::Create(FileStatusTd, data->Get<SByte>(9)->Get()) );

			// Key numbers
			Utility::SplitNumber( data->Get<SByte>(11), b1, b2);
			dirInfo->Set("dirNextKeyNum", b1);

			Utility::SplitNumber( data->Get<SByte>(12), b1, b2);
			dirInfo->Set("deleteKeyNum", b1);
			dirInfo->Set("createKeyNum", b2);

			// numDF and numEF
			dirInfo->Set("numDF", data->Get<SByte>(14) );
			dirInfo->Set("numEF", data->Get<SByte>(15) );
		}
		catch(...)
		{
			throw SPluginException("Error in parsing results from GetResponse()");
		}				
	}
	else
	{
		// The current file is an Elementary File
		try
		{
			SNumberSp fileSize = Utility::BuildNumber( data->Get<SByte>(0), data->Get<SByte>(1) );
			fileInfo->Set("fileSize", fileSize);

			SNumberSp fileID = Utility::BuildNumber( data->Get<SByte>(2), data->Get<SByte>(3) );
			fileInfo->Set("fileID", fileID);

			fileInfo->Set("fileType", SEnum::Create(FileTypeTd, data->Get<SByte>(4)->Get() ) );

			// restrictions
			//unsigned char r = data->Get<SByte>(7)->Get() && 0x03;
			//if ( r == 0 ) fileInfo->Set("restrictions", SEnum::Create(CommandRestrictionsTd, "NoDecreaseNoIncrease") );
			//if ( r == 2 ) fileInfo->Set("restrictions", SEnum::Create(CommandRestrictionsTd, "DecreaseOnly") );
			//if ( r == 1 ) fileInfo->Set("restrictions", SEnum::Create(CommandRestrictionsTd, "IncreaseOnly") );
			//if ( r == 3 ) fileInfo->Set("restrictions", SEnum::Create(CommandRestrictionsTd, "DecreaseIncrease") );

			//fileInfo->Set("restrictions", 
			//	SEnum::Create(CommandRestrictionsTd, r ) );

			// ACs
			SByteSp b1;
			SByteSp b2;

			Utility::SplitNumber( data->Get<SByte>(6), b1, b2);
			fileInfo->Set("readBinarySeekAC", 
				SEnum::Create(AccessConditionTd, b1->Get() ));
			fileInfo->Set("updateBinaryDecreaseAC", 
				SEnum::Create(AccessConditionTd, b2->Get() ));

			Utility::SplitNumber( data->Get<SByte>(7), b1, b2);
			fileInfo->Set("increaseAC", 
				SEnum::Create(AccessConditionTd, b1->Get() ));
			fileInfo->Set("createRecordAC", 
				SEnum::Create(AccessConditionTd, b2->Get() ));

			Utility::SplitNumber( data->Get<SByte>(8), b1, b2);
			fileInfo->Set("rehabilitateAC", 
				SEnum::Create(AccessConditionTd, b1->Get() ));
			fileInfo->Set("invalidateAC", 
				SEnum::Create(AccessConditionTd, b2->Get() ));

			// fileStatus
			fileInfo->Set("fileStatus",	SEnum::Create(FileStatusTd, data->Get<SByte>(9)->Get()) );

			// Key numbers
			Utility::SplitNumber( data->Get<SByte>(11), b1, b2);
			fileInfo->Set("readBinarySeekKeyNum", b1);
			fileInfo->Set("updateBinaryDecreaseKeyNum", b2);

			Utility::SplitNumber( data->Get<SByte>(12), b1, b2);
			fileInfo->Set("increaseKeyNum", b1);
			fileInfo->Set("createRecordKeyNum", b2);

			Utility::SplitNumber( data->Get<SByte>(13), b1, b2);
			fileInfo->Set("rehabilitateKeyNum", b1);
			fileInfo->Set("invalidateKeyNum", b2);

			if ( data->Get<SByte>(4)->Get() != 0x01 )
			{
				// The file is not transparent
				fileInfo->Set("recordLength", data->Get<SByte>(14));				
				fileInfo->Set("numRecords", data->Get<SByte>(15));				
			}
		}
		catch(...)
		{
			throw SPluginException("Error in parsing results from GetResponse().");
		}
	}

	// Settings outputs
	outputs->Set("dirInfo", dirInfo);
	outputs->Set("fileInfo", fileInfo);

	BYTE sw1 = (BYTE) data->Get<SByte>(16)->Get();
	BYTE sw2 = (BYTE) data->Get<SByte>(17)->Get();
	SetCommonOutputs(sw1, sw2);
}

void Cryptoflex8Plugin::Cryptoflex8_Invalidate()
{
	// Insert plugin operations here
	string input = "0xF004000000";
	SVectorSp receiveBuffer = ExecutePcscTransmit(input);

	// Setting outputs
	BYTE sw1 = (BYTE) receiveBuffer->Get<SByte>(0)->Get();
	BYTE sw2 = (BYTE) receiveBuffer->Get<SByte>(1)->Get();
	SetCommonOutputs(sw1, sw2);
}

void Cryptoflex8Plugin::Cryptoflex8_ReadBinary()
{
	// Input preparation   
	SNumberSp offset  = inputs->Get<SNumber>("offset");
	SByteSp length  = inputs->Get<SByte>("length");

	BYTE offsetMSB = offset->Get() / 256;
	BYTE offsetLSB = offset->Get() % 256;
	(*vars)["offsetMSB"] = SByte::Create(offsetMSB);
	(*vars)["offsetLSB"] = SByte::Create(offsetLSB);
	(*vars)["length"] = length;

	// Insert plugin operations here
	string input = Utility::Format("0xC0B0{0,2nx}{1,2nx}{2,2nx}", "offsetMSB offsetLSB length", vars);
	SVectorSp receiveBuffer = ExecutePcscTransmit(input);

	// Setting outputs
	SVectorSp data = SVector::Create( descriptorsRepository->GetVectorTypeDescriptor("pcsc.Buffer") );

	int len = receiveBuffer->GetLength();
	if ( len > 2)
	{
		for(int i=0; i<(int)receiveBuffer->GetLength()-2; ++i)
		{
			data->AddItem( receiveBuffer->Get(i) );
		}
	}

	outputs->Set("data", data);

	BYTE sw1 = (BYTE) receiveBuffer->Get<SByte>(len-2)->Get();
	BYTE sw2 = (BYTE) receiveBuffer->Get<SByte>(len-1)->Get();
	SetCommonOutputs(sw1, sw2);
}

void Cryptoflex8Plugin::Cryptoflex8_Rehabilitate()
{
	string input = "0xF044000000";

	SVectorSp receiveBuffer = ExecutePcscTransmit(input);

	// Setting outputs
	BYTE sw1 = (BYTE) receiveBuffer->Get<SByte>(0)->Get();
	BYTE sw2 = (BYTE) receiveBuffer->Get<SByte>(1)->Get();
	SetCommonOutputs(sw1, sw2);
}

void Cryptoflex8Plugin::Cryptoflex8_RetrieveCLA()
{
	outputs->Set("CLA", SByte::Create( 0xC0 ));
}

void Cryptoflex8Plugin::Cryptoflex8_Select()
{
	// Insert plugin operations here
	string input = Utility::Format("0xC0A4000002{0,4nx}", "id", inputs);
	SVectorSp receiveBuffer = ExecutePcscTransmit(input);

	// Setting outputs
	BYTE sw1 = (BYTE) receiveBuffer->Get<SByte>(0)->Get();
	BYTE sw2 = (BYTE) receiveBuffer->Get<SByte>(1)->Get();
	SetCommonOutputs(sw1, sw2);
}

void Cryptoflex8Plugin::Cryptoflex8_SelectDF()
{
	// Input preparation   
	string input = Utility::Format("0xC0A4000002{0,4nx}", "id", inputs);
	SVectorSp receiveBuffer = ExecutePcscTransmit(input);

	BYTE sw1 = (BYTE) receiveBuffer->Get<SByte>(0)->Get();
	BYTE sw2 = (BYTE) receiveBuffer->Get<SByte>(1)->Get();

	SStructSp dirSelectInfo = SStruct::Create( DirSelectInfoTd );

	if ( sw1 == 0x61 )
	{
		// Get more info with the GetResponse command
		SVectorSp data = ExecuteGetResponse( receiveBuffer->Get<SByte>(1)->ToString("d") );

		sw1 = 0x90;
		sw2 = 0x00;

		// Set the dirSelectInfo members
		try {

			SNumberSp remainedSpace = Utility::BuildNumber( data->Get<SByte>(2), data->Get<SByte>(3) );
			dirSelectInfo->Set("remainedSpace", remainedSpace);

			SNumberSp fileID = Utility::BuildNumber( data->Get<SByte>(4), data->Get<SByte>(5) );
			dirSelectInfo->Set("fileID", fileID);

			// ACs
			SByteSp b1;
			SByteSp b2;
			Utility::SplitNumber( data->Get<SByte>(8), b1, b2);
			dirSelectInfo->Set("dirNextAC", 
				SEnum::Create(AccessConditionTd, (unsigned long) b1->Get() ));

			Utility::SplitNumber( data->Get<SByte>(9), b1, b2);
			dirSelectInfo->Set("deleteAC", 
				SEnum::Create(AccessConditionTd, (unsigned long) b1->Get() ));
			dirSelectInfo->Set("createAC", 
				SEnum::Create(AccessConditionTd, (unsigned long) b2->Get() ));
	
			// fileStatus
			dirSelectInfo->Set("fileStatus", 
				SEnum::Create(FileStatusTd, data->Get<SByte>(11)->Get()) );

			// chvData
			unsigned char chvDataLength = data->Get<SByte>(12)->Get();
			dirSelectInfo->Set("chvData", 
				SEnum::Create(ChvDataTd, chvDataLength) );

			// chv1Status (Directory Characteristics???)
			//Utility::SplitNumber( data->Get<SByte>(13), b1, b2);
			//dirSelectInfo->Set("chv1Status",
			//	SEnum::Create(ChvStatusTd, b1->Get() ) );

			// numDF and numEF
			dirSelectInfo->Set("numDF", data->Get<SByte>(14) );
			dirSelectInfo->Set("numEF", data->Get<SByte>(15) );

			// availablePINs
			dirSelectInfo->Set("availablePINs", 
				SEnum::Create(AvailablePINsTd, data->Get<SByte>(16)->Get()) );

			if ( chvDataLength > 5)
			{
				// remainedCHV1 and remainedUnblockCHV1
				Utility::SplitNumber( data->Get<SByte>(18), b1, b2);
				dirSelectInfo->Set("remainedCHV1", b2 );

				Utility::SplitNumber( data->Get<SByte>(19), b1, b2);
				dirSelectInfo->Set("remainedUnblockCHV1", b2 );

				dirSelectInfo->Set("chv1Status", 
					SEnum::Create(ChvStatusTd, b1->Get() ) );			
			}

			if ( chvDataLength > 7)
			{
				// remainedCHV2
				Utility::SplitNumber( data->Get<SByte>(20), b1, b2);
				dirSelectInfo->Set("remainedCHV2", b2 );

				// remainedUnblockCHV2
				Utility::SplitNumber( data->Get<SByte>(21), b1, b2);
				dirSelectInfo->Set("remainedUnblockCHV2", b2 );

				dirSelectInfo->Set("chv2Status", 
					SEnum::Create(ChvStatusTd, b1->Get() ) );
			}			
		}
		catch(...)
		{
			throw SPluginException("Error in parsing results from GetResponse(). Maybe the file is not a directory.");
		}
	}

	// Setting outputs
	outputs->Set("dirSelectInfo", dirSelectInfo);

	SetCommonOutputs(sw1, sw2);
}

void Cryptoflex8Plugin::Cryptoflex8_SelectEF()
{
	// Input preparation   
	string input = Utility::Format("0xC0A4000002{0,4nx}", "id", inputs);
	SVectorSp receiveBuffer = ExecutePcscTransmit(input);

	BYTE sw1 = (BYTE) receiveBuffer->Get<SByte>(0)->Get();
	BYTE sw2 = (BYTE) receiveBuffer->Get<SByte>(1)->Get();

	SStructSp fileInfo = SStruct::Create( FileInfoTd );

	if ( sw1 == 0x61 )
	{
		// Get more info with the GetResponse command
		SVectorSp data = ExecuteGetResponse( receiveBuffer->Get<SByte>(1)->ToString("d") );

		sw1 = 0x90;
		sw2 = 0x00;

		// Set the fileInfo members
		try {

			SNumberSp fileSize = Utility::BuildNumber( data->Get<SByte>(2), data->Get<SByte>(3) );
			fileInfo->Set("fileSize", fileSize);

			SNumberSp fileID = Utility::BuildNumber( data->Get<SByte>(4), data->Get<SByte>(5) );
			fileInfo->Set("fileID", fileID);

			fileInfo->Set("fileType", SEnum::Create(FileTypeTd, data->Get<SByte>(6)->Get() ) );

			// restrictions
			unsigned char r = data->Get<SByte>(7)->Get() && 0x03;
			if ( r == 0 ) fileInfo->Set("restrictions", SEnum::Create(CommandRestrictionsTd, "NoDecreaseNoIncrease") );
			if ( r == 2 ) fileInfo->Set("restrictions", SEnum::Create(CommandRestrictionsTd, "DecreaseOnly") );
			if ( r == 1 ) fileInfo->Set("restrictions", SEnum::Create(CommandRestrictionsTd, "IncreaseOnly") );
			if ( r == 3 ) fileInfo->Set("restrictions", SEnum::Create(CommandRestrictionsTd, "DecreaseIncrease") );

			fileInfo->Set("restrictions", 
				SEnum::Create(CommandRestrictionsTd, r ) );

			// ACs
			SByteSp b1;
			SByteSp b2;

			Utility::SplitNumber( data->Get<SByte>(8), b1, b2);
			fileInfo->Set("readBinarySeekAC", 
				SEnum::Create(AccessConditionTd, b1->Get() ));
			fileInfo->Set("updateBinaryDecreaseAC", 
				SEnum::Create(AccessConditionTd, b2->Get() ));

			Utility::SplitNumber( data->Get<SByte>(9), b1, b2);
			fileInfo->Set("increaseAC", 
				SEnum::Create(AccessConditionTd, b1->Get() ));
			fileInfo->Set("createRecordAC", 
				SEnum::Create(AccessConditionTd, b2->Get() ));

			Utility::SplitNumber( data->Get<SByte>(10), b1, b2);
			fileInfo->Set("rehabilitateAC", 
				SEnum::Create(AccessConditionTd, b1->Get() ));
			fileInfo->Set("invalidateAC", 
				SEnum::Create(AccessConditionTd, b2->Get() ));

			// fileStatus
			fileInfo->Set("fileStatus", 
				SEnum::Create(FileStatusTd, data->Get<SByte>(11)->Get()) );

			// chvData
			unsigned char dataLength = data->Get<SByte>(12)->Get();

			if ( dataLength == 2 )
			{
				fileInfo->Set("recordLength", data->Get<SByte>(14) );				
			}
		}
		catch(...)
		{
			throw SPluginException("Error in parsing results from GetResponse(). Maybe the file is not a EF.");
		}
	}

	// Setting outputs
	outputs->Set("fileInfo", fileInfo);

	SetCommonOutputs(sw1, sw2);
}

void Cryptoflex8Plugin::Cryptoflex8_UnblockCHV()
{
	// Insert plugin operations here
	string input = Utility::Format("0xF02C00{0,2nx}10{1,8nx}{2,8nx}", "chvType unblockCHV newCHV", inputs);
	SVectorSp receiveBuffer = ExecutePcscTransmit(input);

	// Setting outputs
	BYTE sw1 = (BYTE) receiveBuffer->Get<SByte>(0)->Get();
	BYTE sw2 = (BYTE) receiveBuffer->Get<SByte>(1)->Get();
	SetCommonOutputs(sw1, sw2);
}

void Cryptoflex8Plugin::Cryptoflex8_UpdateBinary()
{
	// Input preparation   
	SNumberSp offset  = inputs->Get<SNumber>("offset");
	SByteSp length  = inputs->Get<SByte>("length");
	SVectorSp data = inputs->Get<SVector>("data");

	BYTE offsetMSB = offset->Get() / 256;
	BYTE offsetLSB = offset->Get() % 256;
	(*vars)["offsetMSB"] = SByte::Create(offsetMSB);
	(*vars)["offsetLSB"] = SByte::Create(offsetLSB);
	(*vars)["length"] = length;
	(*vars)["data"] = data;

	// Insert plugin operations here
	string input = Utility::Format("0xC0D6{0,2nx}{1,2nx}{2,2nx}{3,nx}", "offsetMSB offsetLSB length data", vars);
	SVectorSp receiveBuffer = ExecutePcscTransmit(input);

	// Setting outputs
	BYTE sw1 = (BYTE) receiveBuffer->Get<SByte>(0)->Get();
	BYTE sw2 = (BYTE) receiveBuffer->Get<SByte>(1)->Get();
	SetCommonOutputs(sw1, sw2);
}

void Cryptoflex8Plugin::Cryptoflex8_VerifyCHV()
{
	// Insert plugin operations here
	string input = Utility::Format("0xC02000{0,2nx}08{1,8nx}", "chvType chvKey", inputs);
	SVectorSp receiveBuffer = ExecutePcscTransmit(input);

	// Setting outputs
	BYTE sw1 = (BYTE) receiveBuffer->Get<SByte>(0)->Get();
	BYTE sw2 = (BYTE) receiveBuffer->Get<SByte>(1)->Get();
	SetCommonOutputs(sw1, sw2);
}

void Cryptoflex8Plugin::Cryptoflex8_VerifyKey()
{
	// Insert plugin operations here
	string input = Utility::Format("0xF02A00{0,2nx}08{1,8nx}", "chvType chvKey", inputs);
 	SVectorSp receiveBuffer = ExecutePcscTransmit(input);

	// Setting outputs
	BYTE sw1 = (BYTE) receiveBuffer->Get<SByte>(0)->Get();
	BYTE sw2 = (BYTE) receiveBuffer->Get<SByte>(1)->Get();
	SetCommonOutputs(sw1, sw2);
}


SVectorSp Cryptoflex8Plugin::ExecutePcscTransmit( string input )
{
	CommandDescriptorSp transmitCd = descriptorsRepository->GetCommandDescriptor("pcsc.Transmit");
	CommandSp transmitCmd = Command::Create(transmitCd, input, vars);
	(*plugins)["pcsc"]->ExecuteCommand(transmitCmd);
	
	SStructSp transmitOutputs = transmitCmd->GetOutputs();

	SEnumSp res = transmitOutputs->Get<SEnum>("res");
	SVectorSp receiveBuffer = transmitOutputs->Get<SVector>("receiveBuffer");

	if ( res->Get() != "SCARD_S_SUCCESS")
	{
		throw SPluginException("There is not a valid pcsc connection extablished.");
	}

	return receiveBuffer;
}

SVectorSp Cryptoflex8Plugin::ExecuteGetResponse( string input )
{
	CommandDescriptorSp getResponseCd = descriptorsRepository->GetCommandDescriptor("iso7816.GetResponse");
	
	(*vars)["CLA"] = SByte::Create(0xC0);

	CommandSp getResponseCmd = Command::Create(getResponseCd, input, vars);

	(*plugins)["iso7816-4"]->ExecuteCommand(getResponseCmd);

	SStructSp getResponseOutputs = getResponseCmd->GetOutputs();

	BYTE sw1 = (BYTE) getResponseOutputs->Get<SByte>("sw1")->Get();
	//BYTE sw2 = (BYTE) getResponseOutputs->Get<SByte>("sw2")->Get();
	SVectorSp data = getResponseOutputs->Get<SVector>("data");

	if ( sw1 != 0x90 )
	{
		throw SPluginException("Impossible to get more information about GetResponse().");
	}

	return data;
}

std::string Cryptoflex8Plugin::GetStatusDescription( BYTE sw1, BYTE sw2 )
{
	unsigned int val = sw1;
	val = val * 256 + sw2;

	if ( sw1 == 0x61 ) return "sw2 bytes of the response data";
	if ( sw1 == 0x67 ) return "Incorrect P3 | Length of the input data is different from the record length | Length of the input data is too long";

	if ( val == 0x6281 ) return "Data possibly corrupted.";
	if ( val == 0x6283 ) return "File already invalidated | File not invalidated | Not permitted, EF or DF invalidated";
	if ( val == 0x6300 ) return "Authentication failed (CHV or Key rejected, or wrong cryptogram)";
	if ( val == 0x6581 ) return "Memory problem";
	if ( val == 0x6981 ) return "No EF exists, or no CHV or key defined in file";
	if ( val == 0x6982 ) return "Access conditions not met";
	if ( val == 0x6983 ) return "All CHV or external key attempts used; futher attempts blocked";
	if ( val == 0x6985 ) return "No Get Challenge before";
	if ( val == 0x6986 ) return "A DF or cyclic EF is selected | No EF selected";
	if ( val == 0x6A80 ) return "File ID already exists in this DF | File type inconsistent with command | Pattern not found or file type inconsistent with command | Record length too long";
	if ( val == 0x6A82 ) return "File ID not found or no more files in current DF";
	if ( val == 0x6A83 ) return "Out of range or record not found";
	if ( val == 0x6A84 ) return "Insufficient space available";
	if ( val == 0x6B00 ) return "Incorrect P1P2 | Offset out of the EF/incorrect mode";
	if ( val == 0x6D00 ) return "Unknown command";
	if ( val == 0x6E00 ) return "Incorrect class for command";
	if ( val == 0x6F00 ) return "Unidentified technical problem";
	if ( val == 0x9000 ) return "Normal ending of command";
	if ( val == 0x9850 ) return "Decrement cannot be performed, minimum value reached | Increase cannot be performed, maximum value reached";

	return "No info available about status.";
}

void Cryptoflex8Plugin::SetCommonOutputs( BYTE sw1, BYTE sw2 )
{
	outputs->Set("sw1", SByte::Create(sw1) );
	outputs->Set("sw2", SByte::Create(sw2) );
	outputs->Set("desc", SString::Create( GetStatusDescription(sw1, sw2) ) );
}
